/*
 * @Author: SJChen
 * @Date: 2021-09-16 22:09:09
 * @LastEditTime: 2022-01-10 00:32:29
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /iEDA/src/iTDP/cellMoveEngine/subGradient/QuadraticTDP.h
 */
#pragma once

#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#include <cfloat>

// #include "TimingSubGradient.hh"
#include "Point.hh"
#include "contest_db.h"
#include "math/lnalg.h"
#include "math/scrm.h"

namespace ieda_contest {

// 待办
enum CostType
{
  COST_NONE,
  COST_WIRELENGTH,
  COST_RC,
  COST_LOCAL_DELAY,
};
enum Mode
{
  MODE_NODE,
  LATE,
  ERALY,
};

class DrivenStrengthCellMovement;
// class ista::TimingEngine;

class QuadraticTDP
{
  friend class DrivenStrengthCellMovement;

 public:
  QuadraticTDP() = delete;
  QuadraticTDP(ContestDB* database);
  QuadraticTDP(const QuadraticTDP&) = delete;
  QuadraticTDP(QuadraticTDP&&) = delete;
  ~QuadraticTDP() = default;

  QuadraticTDP& operator=(const QuadraticTDP&) = delete;
  QuadraticTDP& operator=(QuadraticTDP&&) = delete;

  void runQuadraticTDPOpt();
  void runCellFix();

  double getCellWorstSlack(ContestInstance* instance);
  double getCellWorstSlack1(ContestInstance* instance);

  double findPinSlack(ContestPin* pin);
  bool moveCell(ContestInstance* cell, const double x, const double y);


 private:
  ContestDB* _contest_database;
  
  std::unordered_map<ContestPin*,double> pin2slack_map;
  std::vector<ContestInstance*> needFixMovableCells;
  std::vector<ContestInstance*> mapIndexToCell;
  std::map<ContestInstance*, int> mapCellToIndex;
  vector<ContestPin*> _most_critical_endpoints;
  std::set<ContestPin*> _timing_endpoints;
  std::set<ContestPin*> _timing_startpoints;
  std::map<ContestNet*, int32_t> _net_to_topo_id;
  std::map<int32_t, ContestNet*> _topo_id_to_net;

  std::vector<double> px;  // Current solution for x,索引是cell id。元素为对应的cell的x坐标
  std::vector<double> py;  // Current solution for y
  std::vector<double> bx;  // Fixed forces for x
  std::vector<double> by;  // Fixed forces for y
  std::vector<double> fw;  // Anchor cells sum of weights
  std::vector<double> fx;  // Anchor cells for x
  std::vector<double> fy;  // Anchor cells for y

  bool _enableRC{false};
  bool _enableMinDisplacement{false};
  bool _EnableMaxDisplacementConstraint{true};
  double minimumResistance{0.01};
  float alpha{0};
  float beta;
  int32_t _MaxDisplacement {50};
  int _maxCentrality{0};
  double medianCriticality;
  double wns{DBL_MAX};
  double wns_early;

  SquareCompressedRowMatrix a;  // Hessian matrix
  SparseMatrixDescriptor dscp;

  double clsQualityScore;
  double clsTDPQualityScore;
  double clsBestQualityScore;

  std::vector<Point<int32_t>> initialPositions;
  // debug
  int _move_success_nums{0};
  int _iter{0};
  int _iter1{0};
  double _original_tns;
  double _original_wns;
  double _original_tns1;
  double _original_wns1;


  // quadratic flow
  void defaultFlow();

  // quadratic methods
  void runPathStraighteningFlow();

  void markAsMovableAllNonFixedCells(const bool stress);
  void buildMapping();
  void copyCellsLocationFromSessionToLinearSystem();    // 把session中的cell复制到线性系统中。
  void buildLinearSystem(const bool stress);            // 建立线性系统
  void neutralizeSystemWithSpreadingForces();           // 中和系统
  void generateStraighteningForces(const bool stress);  // 对关键路径加线加权
  void solveLinearSystem(bool spreadingForces);  // 再次求解线性系统
  void copyCellsLocationFromLinearSystemToSession();

  // utility
  bool canBeSetAsMovable(ContestInstance* cell, const bool considerCriticalCellsAsFixed);
  void removeAnchors();
  double getNetCriticality(ContestNet* net);
  double getPinCriticality(ContestPin* pin);
  double getSmoothedCriticality(ContestPin* pin);
  double getPinCentrality(ContestPin* pin);
  double getPinEarlyCriticality(ContestPin* pin);
  double getCellCriticality(ContestInstance* cell,Mode timing_mode);


  bool checkMaxDisplacementViolation(const std::string& errorMsg, const bool hideMessageIfPass);
  bool checkUnlegalizedCells();
  void obtainTopCriticalEndpoints(vector<ContestPin*>& most_critical_endpoints, int endpoint_nums);
  double computeManhattanDistance(Point<int32_t> driverPos, Point<int32_t> sinkPos);
  double getPinImportance(ContestPin* pin);
  bool moveCellWithCostEvaluation(ContestInstance* cell, const int32_t x, const int32_t y, double& oldCost, double& newCost,CostType costFunction);
  bool violateMaxDisplacement(ContestInstance* cell);
  bool translateCellWithCostEvaluation(ContestInstance* cell, const int32_t x, const int32_t y, double& oldCost, double& newCost);
  double computeCost(ContestInstance* cell, const CostType costFunction);
  double computeCost_Wirelength(ContestInstance* cell);
  double computeCost_RC(ContestInstance* cell);
  double computeCost_LocalDelay(ContestInstance* cell);
  void computeCost_UpdateAfterMove(ContestInstance* cell, CostType costFunction);
  double getPinResistance(ContestPin* driver);
  double getPinCap(ContestPin* pin);
  bool approximatelyEqual(const double a, const double b, const double precision = 1e-6f);
  bool doCellBalancing(ContestInstance* cell);
  double computePathManhattanLength(const std::vector<ContestPin*>& path);

  bool moveCellWithCostEvaluationCached(ContestInstance* cell, const int32_t x, const int32_t y, const CostType costFunction,
                                        const double oldCost, double& newCost);
  void obtainOnePathPin(ContestPin* endpoint, std::vector<ContestPin*>& need_fix_instances);
  void computeBoundedPosition(ContestInstance* cell, const int32_t x, const int32_t y, int32_t& boundedx, int32_t& boundedy);
  int32_t getMaxDisplacement() const { return _MaxDisplacement * _contest_database->get_ContestLayout()->get_database_unit(); }
  bool placeCell(ContestInstance* cell ,const int32_t boundedx,const int32_t boundedy);
  void obtainEndpoints();
  void buildNetTopo();
  bool isFlipflopDataInput(ContestPin* pin);
  void updateTimingCentrality();
  void updateTiming_Centrality_Net(ContestNet* net);
};
}  // namespace ipl
